package action.chap11;

import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * @CLassName BestPriceFinder
 * @Description  CompletableFuture详解 https://segmentfault.com/a/1190000018755486
 * @Author 孙峰
 * @Date 2020/2/10 22:41
 * @Version 1.0
 */
public class BestPriceFinder {

    private final List<Shop> shops = Arrays.asList(new Shop("BestPrice"),
            new Shop("LetsSaveBig"),
            new Shop("MyFavoriteShop"),
            new Shop("BuyItAll"),
            new Shop("ShopEasy"),
            new Shop("ShopEasy"),
            new Shop("ShopEasy"),
            new Shop("ShopEasy"),
            new Shop("ShopEasy"),
            new Shop("ShopEasy"),
            new Shop("ShopEasy"),
            new Shop("ShopEasy"),
            new Shop("ShopEasy"),
            new Shop("ShopEasy"),
            new Shop("ShopEasy"));

    private final Executor executor = Executors.newFixedThreadPool(shops.size(), new ThreadFactory() {
        @Override
        public Thread newThread(Runnable r) {
            Thread t = new Thread(r);
            t.setDaemon(true);
            return t;
        }
    });

    //这个是顺序流，按照顺序流每个任务的执行时间执行 map接受的是一个Function  传进一个值转换成另外个值
    public List<String> findPricesSequential(String product) {
        return shops.stream()
                .map(shop -> shop.getPrice(product))
                .map(Quote::parse)
                .map(Discount::applyDiscount)
                .collect(Collectors.toList());
    }

    // 并行流，并行流的执行和机器的处理核心数有关，这个不能动态配置最大线程数
    public List<String> findPricesParallel(String product) {
        return shops.parallelStream()
                .map(shop -> shop.getPrice(product))
                .map(Quote::parse)
                .map(Discount::applyDiscount)
                .collect(Collectors.toList());
    }

    //CompletableFuture版本可以动态设置线程池的，线程数量根据任务数量动态配置线程池的最大线程数是比较合理的
    public List<String> findPricesFuture(String product) {
        // 获取CompletableFuture的流
        Stream<CompletableFuture<String>> pricesStream = findPricesStream(product);
        //将流式获取CompletableFuture的流转换成list集合
        List<CompletableFuture<String>> collect = pricesStream.collect(Collectors.toList());
        //转换成list集合成为字符串
        return collect.stream().map(CompletableFuture::join).collect(Collectors.toList());
    }

    /**
     *  方法的引用
     *  Quote 是报价的意思，每个商店的报价是根据商店的
     *  名称，商品的价格以及折扣计算出来的   这三个属性存在的位置就是Quote
     * @param product
     * @return
     */
    public Stream<CompletableFuture<String>> findPricesStream(String product) {
        return shops.stream()
                // CompletableFuture.supplyAsync 异步处理获取到一个future对象
                .map(shop -> CompletableFuture.supplyAsync(() -> shop.getPrice(product), executor))
                //thenApply（）转换的是泛型中的类型，是同一个CompletableFuture；
                //thenApply 是进行流转换  是对流的转换  根据传进来的对象转换成另外的对象
                .map(future -> future.thenApply(Quote::parse))
                //thenCompose（）用来连接两个CompletableFuture，是生成一个新的CompletableFuture。
                //thenCompose（）在异步操作完成的时候对异步操作的结果进行一些操作，
                // 并且仍然返回CompletableFuture类型，相当于flatMap，用来连接两个CompletableFuture
                //CompletableFuture API 的最佳场景是能够在一系列计算步骤中组合CompletableFuture实例。
                .map(future -> future.thenCompose(quote -> CompletableFuture.supplyAsync(() -> Discount.applyDiscount(quote), executor)));
    }

    public void printPricesStream(String product) {
        long start = System.nanoTime();
        CompletableFuture[] futures = findPricesStream(product)
                .map(f -> f.thenAccept(s -> System.out.println(s + " (done in " + ((System.nanoTime() - start) / 1_000_000) + " msecs)")))
                .toArray(size -> new CompletableFuture[size]);
        CompletableFuture.allOf(futures).join();
        System.out.println("All shops have now responded in " + ((System.nanoTime() - start) / 1_000_000) + " msecs");
    }

}
